#include "U2PredefinedControllers.h"

#include "U2Root.h"
#include "U2Math.h"
#include "U2LogManager.h"
#include "U2TextureUnitState.h"


U2EG_NAMESPACE_USING


//-----------------------------------------------------------------------
// FrameTimeControllerValue
//-----------------------------------------------------------------------
FrameTimeControllerValue::FrameTimeControllerValue()
{
    // Register self
    U2Root::getSingleton().addFrameListener(this);
    mFrameTime = 0;
	mTimeFactor = 1;
	mFrameDelay = 0;
    mElapsedTime = 0;

}
//-----------------------------------------------------------------------
bool FrameTimeControllerValue::frameStarted(const FrameEvent &evt)
{
	if(mFrameDelay) 
	{
		// Fixed frame time
		mFrameTime = mFrameDelay;
		mTimeFactor =  mFrameDelay / evt.timeSinceLastFrame;
	}
	else 
	{
		// Save the time value after applying time factor
		mFrameTime = mTimeFactor * evt.timeSinceLastFrame;
	}
    // Accumulate the elapsed time
    mElapsedTime += mFrameTime;
    return true;
}
//-----------------------------------------------------------------------
bool FrameTimeControllerValue::frameEnded(const FrameEvent &evt)
{
    return true;
}
//-----------------------------------------------------------------------
u2real FrameTimeControllerValue::getValue() const
{
    return mFrameTime;
}
//-----------------------------------------------------------------------
void FrameTimeControllerValue::setValue(u2real value)
{
    // Do nothing - value is set from frame listener
}
//-----------------------------------------------------------------------
u2real FrameTimeControllerValue::getTimeFactor(void) const {
	return mTimeFactor;
}
//-----------------------------------------------------------------------
void FrameTimeControllerValue::setTimeFactor(u2real tf) {
	if(tf >= 0) 
	{
		mTimeFactor = tf;
		mFrameDelay = 0;
	}
}
//-----------------------------------------------------------------------
u2real FrameTimeControllerValue::getFrameDelay(void) const {
	return mFrameDelay;
}
//-----------------------------------------------------------------------
void FrameTimeControllerValue::setFrameDelay(u2real fd) {
	mTimeFactor = 0;
	mFrameDelay = fd;
}
//-----------------------------------------------------------------------
u2real FrameTimeControllerValue::getElapsedTime(void) const
{
    return mElapsedTime;
}
//-----------------------------------------------------------------------
void FrameTimeControllerValue::setElapsedTime(u2real elapsedTime)
{
    mElapsedTime = elapsedTime;
}
//-----------------------------------------------------------------------
// TextureFrameControllerValue
//-----------------------------------------------------------------------
TextureFrameControllerValue::TextureFrameControllerValue(TextureUnitState* t)
{
    mTextureLayer = t;
}
//-----------------------------------------------------------------------
u2real TextureFrameControllerValue::getValue(void) const
{
    int numFrames = mTextureLayer->getNumFrames();
    return ((u2real)mTextureLayer->getCurrentFrame() / (u2real)numFrames);
}
//-----------------------------------------------------------------------
void TextureFrameControllerValue::setValue(u2real value)
{
    int numFrames = mTextureLayer->getNumFrames();
    mTextureLayer->setCurrentFrame((int)(value * numFrames) % numFrames);
}
//-----------------------------------------------------------------------
// TexCoordModifierControllerValue
//-----------------------------------------------------------------------
TexCoordModifierControllerValue::TexCoordModifierControllerValue(TextureUnitState* t,
    bool translateU, bool translateV, bool scaleU, bool scaleV, bool rotate )
{
    mTextureLayer = t;
    mTransU = translateU;
    mTransV = translateV;
    mScaleU = scaleU;
    mScaleV = scaleV;
    mRotate = rotate;
}
//-----------------------------------------------------------------------
u2real TexCoordModifierControllerValue::getValue() const
{
    /**********************@@@@@@@@@@@@@@@@@@@@@
    const Matrix4& pMat = mTextureLayer->getTextureTransform();
    if (mTransU)
    {
        return pMat[0][3];
    }
    else if (mTransV)
    {
        return pMat[1][3];
    }
    else if (mScaleU)
    {
        return pMat[0][0];
    }
    else if (mScaleV)
    {
        return pMat[1][1];
    }
    */
    // Shouldn't get here
    return 0;
}
//-----------------------------------------------------------------------
void TexCoordModifierControllerValue::setValue(u2real value)
{
    if (mTransU)
    {
        mTextureLayer->setTextureUScroll(value);
    }
    if (mTransV)
    {
        mTextureLayer->setTextureVScroll(value);
    }
    if (mScaleU)
    {
        mTextureLayer->setTextureUScale(value);
    }
    if (mScaleV)
    {
        mTextureLayer->setTextureVScale(value);
    }
    if (mRotate)
    {
        mTextureLayer->setTextureRotate(Radian(value * U2Math::TWO_PI));
    }
}
//-----------------------------------------------------------------------
// PassthroughControllerFunction
//-----------------------------------------------------------------------
PassthroughControllerFunction::PassthroughControllerFunction(bool delta) 
	: ControllerFunction<u2real>(delta)
{
}
//-----------------------------------------------------------------------
u2real PassthroughControllerFunction::calculate(u2real source)
{
	return getAdjustedInput(source);

}
//-----------------------------------------------------------------------
// AnimationControllerFunction
//-----------------------------------------------------------------------
AnimationControllerFunction::AnimationControllerFunction(u2real sequenceTime, u2real timeOffset) 
	: ControllerFunction<u2real>(false)
{
    mSeqTime = sequenceTime;
    mTime = timeOffset;
}
//-----------------------------------------------------------------------
u2real AnimationControllerFunction::calculate(u2real source)
{
    // Assume source is time since last update
    mTime += source;
    // Wrap
    while (mTime >= mSeqTime) mTime -= mSeqTime;
    while (mTime < 0) mTime += mSeqTime;

    // Return parametric
    return mTime / mSeqTime;
}
//-----------------------------------------------------------------------
void AnimationControllerFunction::setTime(u2real timeVal)
{
	mTime = timeVal;
}
//-----------------------------------------------------------------------
void AnimationControllerFunction::setSequenceTime(u2real seqVal)
{
	mSeqTime = seqVal;
}
//-----------------------------------------------------------------------
// ScaleControllerFunction
//-----------------------------------------------------------------------
ScaleControllerFunction::ScaleControllerFunction(u2real factor, bool delta) : ControllerFunction<u2real>(delta)
{
    mScale = factor;
}
//-----------------------------------------------------------------------
u2real ScaleControllerFunction::calculate(u2real source)
{
    return getAdjustedInput(source * mScale);

}
//-----------------------------------------------------------------------
// WaveformControllerFunction
//-----------------------------------------------------------------------
WaveformControllerFunction::WaveformControllerFunction(WaveformType wType, u2real base,  u2real frequency, u2real phase, u2real amplitude, bool delta, u2real dutyCycle)
    :ControllerFunction<u2real>(delta)
{
    mWaveType = wType;
    mBase = base;
    mFrequency = frequency;
    mPhase = phase;
    mAmplitude = amplitude;
    mDeltaCount = phase;
	mDutyCycle = dutyCycle;
}
//-----------------------------------------------------------------------
u2real WaveformControllerFunction::getAdjustedInput(u2real input)
{
    u2real adjusted = ControllerFunction<u2real>::getAdjustedInput(input);

    // If not delta, adjust by phase here
    // (delta inputs have it adjusted at initialisation)
    if (!mDeltaInput)
    {
        adjusted += mPhase;
    }

    return adjusted;
}
//-----------------------------------------------------------------------
u2real WaveformControllerFunction::calculate(u2real source)
{
    u2real input = getAdjustedInput(source * mFrequency);
    u2real output = 0;
    // For simplicity, factor input down to {0,1)
    // Use looped subtract rather than divide / round
    while (input >= 1.0)
        input -= 1.0;
    while (input < 0.0)
        input += 1.0;

    // Calculate output in -1..1 range
    switch (mWaveType)
    {
    case WFT_SINE:
        output = U2Math::Sin(Radian(input * U2Math::TWO_PI));
        break;
    case WFT_TRIANGLE:
        if (input < 0.25)
            output = input * 4;
        else if (input >= 0.25 && input < 0.75)
            output = 1.0f - ((input - 0.25f) * 4.0f);
        else
            output = ((input - 0.75f) * 4.0f) - 1.0f;

        break;
    case WFT_SQUARE:
        if (input <= 0.5f)
            output = 1.0f;
        else
            output = -1.0f;
        break;
    case WFT_SAWTOOTH:
        output = (input * 2.0f) - 1.0f;
        break;
    case WFT_INVERSE_SAWTOOTH:
        output = -((input * 2.0f) - 1.0f);
        break;
	case WFT_PWM:
		if( input <= mDutyCycle )
			output = 1.0f;
		else
			output = -1.0f;
		break;
    }

    // Scale output into 0..1 range and then by base + amplitude
    return mBase + ((output + 1.0f) * 0.5f * mAmplitude);
}
